﻿(function ($) {
    $.extend($, {
        templateBinding: {
            tag: {
                val: function (data, expression, options) {
                    var b = new Binding();
                    b.target = data;
                    b.expression = expression;
                    this.addBinding(b, options);
                }
            }
        }
    });

    String.prototype.unescapeHtml = function () {
        var dummy = document.createElement("div");
        dummy.innerHTML = this;
        var result = dummy.innerText;
        dummy.innerHtml = null;
        return result;
    }

    var tryParseJSON = function (json, step) {
        if (/&quot;/.test(json))
            return tryParseJSON(json.unescapeHtml(), step);
        if (json.charAt(0) == '{' || json.charAt(0) == '[') {
            try {
                return $.parseJSON(json);
            }
            catch (e) {
                if (step < 1)
                    return tryParseJSON(json.replace(/([,{])([^:]+)\s/g, function ($0, $1, $2) { return $1 + '"' + $2 + '"' }), 1);
                if (step < 2)
                    return tryParseJSON(json.replace(/"?val"?\s*/g, function ($0, $1) { return '"val":"' + $1 + '"' }), 2);
                throw e;
            }
        }
        else {
            return tryParseJSON('{' + json + '}');
        }
    }


    var oldFnTmpl = $.fn.tmpl;
    var oldTmpl = $.tmpl;
    var applyTemplate = function (data) {
        this.filter('[data-bind]').add(this.find('[data-bind]')).each(function (index, item) {
            var $item = $(item);
            var dataBind = $item.attr('data-bind').trim();
            var bindings;
            bindings = tryParseJSON(dataBind);

            if (!$.isArray(bindings))
                bindings = [bindings];
            for (var i = 0; i < bindings.length; i++) {
                for (var key in bindings[i]) {
                    if (key == "format")
                        continue;
                    $.templateBinding.tag[key].call($item, data, bindings[i][key], bindings[i].format);
                }
            }
        });
        return this;
    }
    $.extend($.fn, {
        applyTemplate: applyTemplate,
        tmpl: function (data, options) {
            return applyTemplate.call(oldFnTmpl.apply(this, arguments), data);
        }
    });


    $.extend($, {
        tmpl: function (tmpl, data) {
            if ($.isArray(data)) {
                var result = null;
                for (var i = 0; i < data.length; i++) {
                    var tmp = $.tmpl(tmpl, data[i]);
                    if (result === null)
                        result = tmp;
                    else {
                        result = result.add(tmp);
                    }
                }
                return result;
            }
            else
                return applyTemplate.call(oldTmpl.apply(this, arguments), data);
        }
    });

    $.tmpl.complete = oldTmpl.complete;

})(jQuery);